package com.luo.d3s.ext.event.jdbc;

import com.luo.d3s.core.domain.event.DomainEvent;
import com.luo.d3s.core.domain.event.DomainEventHandler;
import com.luo.d3s.ext.component.bus.DomainRegistry;
import com.luo.d3s.ext.event.jdbc.publish.DomainEventTxPublisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 领域事件分发器
 *
 * @author luohq
 * @date 2023-01-24 16:38
 */
@Component
public class EventBus {

    private static final Logger log = LoggerFactory.getLogger(EventBus.class);

    /**
     * Map(DomainEvent实现类Class, DomainEventHandler实现类实例)
     */
    private static MultiValueMap<Class<? extends DomainEvent>, DomainEventHandler> eventClass2HandlerCache = new LinkedMultiValueMap<>();

    /**
     * 领域事件发布器
     */
    private static DomainEventTxPublisher domainEventTxPublisher;

    @Autowired
    private void setDomainEventTxPublisher(DomainEventTxPublisher domainEventTxPublisher) {
        EventBus.domainEventTxPublisher = domainEventTxPublisher;
    }

    /**
     * 发布领域事件
     *
     * @param domainEvent 领域事件
     */
    public static void publish(DomainEvent domainEvent) {
        EventBus.domainEventTxPublisher.saveAndSend(domainEvent);
    }


    /**
     * 根据领域事件调用（同步）对应的事件处理器实现
     *
     * @param domainEvent 领域事件
     */
    public static void dispatchBatch(DomainEvent domainEvent) {
        //待分发领域事件类型
        Class<? extends DomainEvent> domainEventClass = domainEvent.getClass();
        //根据待分发领域事件类型匹配对应的事件处理器
        List<DomainEventHandler> domainEventHandlers = eventClass2HandlerCache.computeIfAbsent(domainEventClass, domainEventClassInner -> {
            /** 若对应类型的事件处理器列表缓存不存在，则设置对应事件类型的处理器缓存列表 */
            return (List<DomainEventHandler>) DomainRegistry.getBeans(DomainEventHandler.class).values().stream()
                    //匹配事件处理接口机
                    .filter(domainEventHandler -> matchDomainEventHandler(domainEventHandler, domainEventClassInner))
                    .collect(Collectors.toList());
        });
        if (CollectionUtils.isEmpty(domainEventHandlers)) {
            log.error("Domain event handler for {} does not exist!", domainEventClass);
            return;
        }
        //调用所有对应的handler进行事件处理
        domainEventHandlers.forEach(handler -> handler.handle(domainEvent));
    }

    /**
     * 验证handler实现类是否匹配domainEventClass（即handle方法参数类型是否匹配domainEventClass）
     *
     * @param handler          事件处理器实现类
     * @param domainEventClass 事件类型
     * @return 是否匹配
     */
    private static Boolean matchDomainEventHandler(DomainEventHandler handler, Class<? extends DomainEvent> domainEventClass) {
        try {
            Class handlerClass = AopUtils.getTargetClass(handler);
            Method handleMethod = handlerClass.getDeclaredMethod("handle", domainEventClass);
            return null != handleMethod;
        } catch (Throwable e) {
            return false;
        }
    }
}
